package nl.ViewerServer.ServerManager.DataManager.Objects;

import nl.ViewerServer.Exceptions.ParameterException;

import org.apache.log4j.Logger;

// implements Comparable and has compareTo method because the parameter list is sorted for the parameter fingerprint
public class Parameter implements Comparable<Parameter> {
   private static Logger      log             = Logger.getLogger( Parameter.class );

   // the parameterId should actually be final, but the database can also insert a parameter, which would have to modify
   // the parameterId (in Database.registerCustomSessionParameter)
   public transient int       parameterId;
   public final String        parameterName;
   protected transient String parameterGetter = null;
   protected transient String parameterSetter = null;

   public static enum SetterType {
      NONE, ASSIGNMENT, FUNCTIONCALL
   }

   protected transient SetterType setterType     = SetterType.NONE;
   protected Object               parameterValue = null;

   public static enum ParameterType {
      INT, STRING, DOUBLE, BOOLEAN
   }

   protected ParameterType     parameterType      = ParameterType.INT;
   protected boolean           isTuple            = false;
   protected int               parameterLength    = 1;
   protected boolean           isUserViewable     = true;
   protected boolean           isUserModifiable   = true;
   protected transient boolean isSessionParameter = false;
   protected boolean           requiredForSynchronization = false;

   public void setSessionParameter( boolean isSessionParameter ) {
      this.isSessionParameter = isSessionParameter;
   }

   public boolean isSessionParameter() {
      return isSessionParameter;
   }

   private Parameter( int parameterId, String parameterName, ParameterType parameterType, boolean isTuple,
         int parameterLength, Object parameterValue, boolean isUserViewable, boolean isUserModifiable,
         String parameterGetter, String parameterSetter, SetterType setterType, boolean requiredForSynchronization ) throws ParameterException {
      super();
      this.parameterId = parameterId;
      this.parameterName = parameterName;

      this.parameterType = parameterType;
      this.isTuple = isTuple;
      this.parameterLength = parameterLength;

      this.setParameterValue( parameterValue );

      this.isUserViewable = isUserViewable;
      this.isUserModifiable = isUserModifiable;

      this.parameterGetter = parameterGetter;
      this.parameterSetter = parameterSetter;
      this.setterType = setterType;

      this.requiredForSynchronization = requiredForSynchronization;
      // System.out.println( "Parameter created: " + parameterName + "/getter = " + this.parameterGetter
      // + ", hasGetter = " + this.hasGetter() );
   }

   public int compareTo( Parameter p ) {
      return parameterName.compareTo( p.parameterName );
   }

   public static Parameter newInstance( int parameterId, String parameterName, String parameterType, boolean isTuple,
         int parameterLength, String pythonValue, boolean isUserViewable, boolean isUserModifiable,
         String parameterGetter, String parameterSetter, String setterType, boolean requiredForSynchronization ) throws ParameterException {

      ParameterType pType;
      try {
         pType = ParameterType.valueOf( parameterType.toUpperCase() );
      }
      catch( IllegalArgumentException iae ) {
         // string to enum failed:
         throw new ParameterException( "Conversion from string to parameterType failed ('" + parameterType
            + "' not found in enum)", iae );
      }
      Object javaObject = JavaPythonUtil.parsePythonObjectToJavaObject( pythonValue );
      Object parameterValue = JavaPythonUtil.convertJavaObjectToParameter( javaObject, pType, parameterLength );
      SetterType sType = SetterType.NONE;

      // only allow a parameter to be set when both the setter type and the setter function are available
      if( setterType != null && parameterSetter != null && parameterSetter.length() > 0 ) {
         try {
            sType = SetterType.valueOf( setterType.toUpperCase() );
            // this will result in an exception when the setterType is incorrect

         }
         catch( IllegalArgumentException iae ) {
            // string to enum failed:
            throw new ParameterException( "Conversion from string to setterType failed ('" + setterType
               + "' not found in enum)", iae );
         }

      }

      // force isUserModifiable to off, when there is no setter
      if( sType == SetterType.NONE )
         isUserModifiable = false;

      return new Parameter( parameterId, parameterName, pType, isTuple, parameterLength, parameterValue,
            isUserViewable, isUserModifiable, parameterGetter, parameterSetter, sType, requiredForSynchronization );
   }

   public void setUserParameterValue( Object javaValue ) throws ParameterException {
      // enforce user-modifiable status
      if( isUserModifiable() == false )
         throw new ParameterException( "Parameter is non-modifiable" );

      setParameterValue( javaValue );
   }

   public void setParameterValue( Object javaValue ) throws ParameterException {
      parameterValue =
         JavaPythonUtil.convertJavaObjectToParameter( javaValue, this.parameterType, this.parameterLength );
   }

   public boolean hasGetter() {
      return( parameterGetter != null && parameterGetter.length() > 0 );
   }

   public boolean hasSetter() {
      return( setterType != SetterType.NONE );
   }

   public String getPythonRepresentation() {
      try {
         return JavaPythonUtil.parseJavaObjectToPythonObject( parameterValue, isTuple );
      }
      catch( ParameterException pe ) {
         log.error( "Parameter could not be parsed to Python representation, did someone alter the parameterValue?" );
         return ""; // safety precaution
      }
   }

   public String getParameterName() {
      return parameterName;
   }

   public String getParameterGetter() {
      return parameterGetter;
   }

   public String getParameterSetter() {
      return parameterSetter;
   }

   public SetterType getSetterType() {
      return setterType;
   }

   public Object getParameterValue() {
      return parameterValue;
   }

   public ParameterType getParameterType() {
      return parameterType;
   }

   public int getParameterLength() {
      return parameterLength;
   }

   public boolean isUserModifiable() {
      return isUserModifiable;
   }

   public void setUserModifiable( boolean isUserModifiable ) {
      this.isUserModifiable = isUserModifiable;
   }

   public boolean isTuple() {
      return isTuple;
   }

   public boolean isUserViewable() {
      return isUserViewable;
   }

   public void setUserViewable( boolean isUserViewable ) {
      this.isUserViewable = isUserViewable;
   }

}
